package appnumerico.code;

import org.nfunk.jep.JEP;
import org.nfunk.jep.Node;
import org.springframework.stereotype.Service;


@Service
public class IntervalosRaicesService {

	public ResultadoBusquedaIncremental calculoBusquedaIncremental(String funcion, double x0, double dx, int n) {
		
		ResultadoBusquedaIncremental rbi = new ResultadoBusquedaIncremental();
		
		JEP parser = buildJep(funcion);
		
		double y0 = evaluate(x0, parser); 
		
		if (y0 == 0) { 
			rbi.addResultadoIteracion(0, x0, y0);
			rbi.setX0(x0);
			rbi.setX1(x0);
			rbi.setRaiz(true);
			return rbi;
		} else {
			double x1 = x0 + dx;
			double y1 = evaluate(x1, parser);
			rbi.addResultadoIteracion(0, x0, y0);
			
			int cont = 1;
			for (; cont < n && y1 * y0 > 0; cont ++) {
				
				x0 = x1;
				y0 = y1;
				x1 = x0 + dx;
				y1 = evaluate(x1, parser);
				
				rbi.addResultadoIteracion(cont, x0, y0);
			}
			
			rbi.addResultadoIteracion(cont, x1, y1);
			if (y1 == 0) {
				rbi.setRaiz(true);
				rbi.setX1(x1);
				rbi.setX0(x1);
			} 
			if (y1 * y0 < 0) {
				
				rbi.setX0(x0);
				rbi.setX1(x1);
			} 
			return rbi;
		}		
	}

	private JEP buildJep(String funcion) {
		JEP jep = new JEP();
		jep.addStandardFunctions();
		jep.addStandardConstants();
		jep.setImplicitMul(true);
		jep.addVariable("x", 0);
		jep.parseExpression(funcion);
		return jep;
	}
	
	public ResultadoRaiz biseccion(String funcion, long iter, double x0, double x1, double tol) {
		ResultadoRaiz rr = new ResultadoRaiz();
		
		JEP jep = buildJep(funcion);

		double y0 = evaluate(x0, jep);
		double y1 = evaluate(x1, jep);

		if (y0 * y1 > 0) {
			rr.setIntervaloIncorrecto(true);
			return rr;
		} else if (y0 == 0) {
			rr.setRaiz(x0);
			rr.setEncontrada(true);
			return rr;
		} else if (y1 == 0) {
			rr.setRaiz(x1);
			rr.setEncontrada(true);
			return rr;
		}
		double xm = (x0 + x1) / 2;
		double ym = evaluate(xm, jep);
		double error = tol + 1;
		double xa;
		
		rr.addResultadoIteracion(0, x0, x1, xm, ym, null);

		for (int cont = 0; ym != 0 && error >= tol && cont < iter; cont ++) {
			
			xa = xm;
			if (y0 * ym > 0) {
				x0 = xm;
				y0 = ym;
			} else {
				x1 = xm;
				y1 = ym;
			}
			xm = (x0 + x1) / 2;
			ym = evaluate(xm, jep);
			error = Math.abs(xm - xa);
			
			rr.addResultadoIteracion(cont + 1, x0, x1, xm, ym, error);
		}

		calculateResultadoRaiz(xm, tol, rr, error, ym);
		
		return rr;
	}
	
	public Object puntoFijo(String strFx, String strGx, long iter, double x0, double tol) {
		ResultadoRaiz rr = new ResultadoRaiz();
		
		JEP fx = buildJep(strFx);
		JEP gx = buildJep(strGx);
    
        double error = tol +1;
        
        double y0 = evaluate(x0, fx);
        rr.addResultadoIteracion(0, x0, y0, null);
        
        for(int contador =0; y0 != 0 && error > tol && contador<iter ;contador++)
        {
        
            double x1 = evaluate(x0, gx);
            double y1 = evaluate(x1, fx);
            error = Math.abs(x1 - x0);

            rr.addResultadoIteracion(contador + 1, x1, y1, error);
            x0 = x1;
        }
           
        calculateResultadoRaiz(x0, tol, rr, error, y0);
		
		return rr;
	}

	private void calculateResultadoRaiz(double x0, double tol, ResultadoRaiz rr, double error, double y0) {
		if(y0 == 0) {
        	rr.setEncontrada(true);
        	rr.setRaiz(x0); 
        } else if(error < tol){
        	rr.setEncontrada(true);
        	rr.setError(error);
        	rr.setRaiz(x0);
        } else {
        	rr.setEncontrada(false);
        	rr.setError(error);
        	rr.setRaiz(x0);
        }
	}
	
	private double evaluate(double value, JEP parser) {
		parser.setVarValue("x", value);
		return parser.getValue();
	}
	
	public static void main(String[] args) throws Exception {
		JEP jep = new JEP();
		jep.setImplicitMul(true);
		jep.addVariable("x", 4);
		
		Node node = jep.parse("x+3");
		
		for (int i = 0; i < 51; i ++) {
		
			jep.setVarValue("x", Double.valueOf(i));
			System.out.println(jep.evaluate(node));
		}
		
	}
	
}
